package com.hss.thread;

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=67#/detail/pc?id=1861

https://kaiwu.lagou.com/course/courseInfo.htm?courseId=67#/detail/pc?id=1862


缓存一致性问题

指令重排

除了缓存一致性问题，还存在另外一种硬件问题，也比较重要：为了使CPU内部的运算单元能够尽量被充分利用，处理器可能会对输入的字节码指令进行重排序处理，也就是处理器优化。除了 CPU 之外，很多编程语言的编译器也会有类似的优化，比如 Java虚拟机的即时编译器（JIT）也会做指令重排。

什么是内存模型

内存模型是一套共享内存系统中多线程读写操作行为的规范，这套规范屏蔽了底层各种硬件和操作系统的内存访问差异，解决了CPU多级缓存、CPU优化、指令重排等导致的内存访问问题，从而保证 Java 程序（尤其是多线程程序）在各种平台下对内存的访问效果一致。


happens-before 先行发生原则
happens-before 用于描述两个操作的内存可见性，通过保证可见性的机制可以让应用程序免于数据竞争干扰。

锁定规则

变量规则

线程启动规则

线程中断规则

线程终结规则

对象终结规则

Java 内存模型应用


Java 内存模型的来源：主要是因为 CPU 缓存和指令重排等优化会造成多线程程序结果不可控。

Java 内存模型是什么：本质上它就是一套规范，在这套规范中有一条最重要的 happens-before 原则。

最后介绍了Java内存模型的使用，其中简单介绍了两种方式：volatile和synchronized。其实除了这两种方式，Java还提供了很多关键字来实现happens-before原则...


synchronized

修饰实例方法；

修饰静态类方法；

修饰代码块。

1个monitorenter和2个monitorexit。这是因为虚拟机需要保证当异常发生时也能释放锁。因此2个monitorexit一个是代码正常执行结束后释放锁，一个是在代码执行异常时释放锁。



synchronized修饰的方法在被编译为字节码后，在方法的flags属性中会被标记为ACC_SYNCHRONIZED标志。
当虚拟机访问一个被标记为 ACC_SYNCHRONIZED 的方法时，会自动在方法的开始和结束（或异常）位置添加 monitorenter 和 monitorexit 指令。

关于 monitorenter 和 monitorexit，可以理解为一把具体的锁。在这个锁中保存着两个比较重要的属性：计数器和指针。

a、计数器代表当前线程一共访问了几次这把锁；

b、指针指向持有这把锁的线程。

ReentrantLock 基本使用

ReentrantLock 的使用同 synchronized 有点不同，它的加锁和解锁操作都需要手动完成，如下所示：

图中 lock() 和 unlock() 分别是加锁和解锁操作。运行效果如下：


公平锁实现

* */
public class Lock {


    public static class LagouSyncMethods {
        private int sum = 0;

        private Object lock = new Object();

        public synchronized void calculate() {
            sum += 1;
        }


        public synchronized void printLog() {
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + " i = " + i);
                    Thread.sleep(300);
                }
            } catch (Exception e) {

            }
        }

        public void printLog2() {

            synchronized (lock) {
                try {

                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + " i = " + i);
                        Thread.sleep(300);
                    }
                } catch (Exception e) {

                }
            }

        }


        public void printLog3() {
            synchronized (this) {
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + " i = " + i);
                        Thread.sleep(300);
                    }
                } catch (Exception e) {

                }
            }

        }

        ReentrantLock reentrantLock = new ReentrantLock();

        public void printLog4() {

            try {
                reentrantLock.lock();
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + " i = " + i);
                    Thread.sleep(300);
                }
            } catch (Exception e) {

            } finally {
                reentrantLock.unlock();
            }
        }






    }

    //公平锁
    static class  lagouFairLockTest implements Runnable{
        private int sharedNumber = 0;

        private static  ReentrantLock lock = new ReentrantLock(true);


        @Override
        public void run() {
            while (sharedNumber <20){
                lock.lock();

                try {
                sharedNumber++;
                System.out.println(Thread.currentThread().getName() + " sharedNumber = " + sharedNumber);
                } catch (Exception e) {

                } finally {
                    lock.unlock();
                }
            }
        }
    }


    static  class  ReadWriteTest{
        private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
        private static String number  = "0";

        public static void main(String[] args) {
            Thread t1 = new Thread(new Reader(),"读线程  1");
            Thread t2 = new Thread(new Reader(),"读线程  2");
            Thread t3 = new Thread(new Writer(),"写线程  1");
            t1.start();
            t2.start();
            t3.start();
        }


        static class Reader implements  Runnable{

            @Override
            public void run() {
                for (int i=0; i<10; i++){
                    lock.readLock().lock();
                    System.out.println(Thread.currentThread().getName() + " number = " + number);
                    lock.readLock().unlock();
                }
            }
        }

        static class  Writer implements Runnable{

            @Override
            public void run() {
                for (int i=1; i<=7; i+=2){
                    lock.writeLock().lock();
                    System.out.println(Thread.currentThread().getName() + " 正在写入 = " + i);
                    number = number.concat(""+i);
                    lock.writeLock().unlock();
                }
            }
        }
    }

    public static void main(String[] args) {

        test5();

    }


    //修饰静态类方法
    static void test1() {
        LagouSyncMethods l1 = new LagouSyncMethods();
//        LagouSyncMethods l2= new LagouSyncMethods();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog();
            }
        });

        t1.start();
        t2.start();
    }

    static void test2() {
        LagouSyncMethods l1 = new LagouSyncMethods();
//        LagouSyncMethods l2= new LagouSyncMethods();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog2();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog2();
            }
        });

        t1.start();
        t2.start();
    }

    static void test3() {
        LagouSyncMethods l1 = new LagouSyncMethods();
        LagouSyncMethods l2 = new LagouSyncMethods();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog3();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog3();
            }
        });

        t1.start();
        t2.start();
    }


    static void test4() {
        LagouSyncMethods l1 = new LagouSyncMethods();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog4();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                l1.printLog4();
            }
        });

        t1.start();
        t2.start();
    }


    static void test5() {
        lagouFairLockTest  lft = new lagouFairLockTest();
        Thread t1 = new Thread(lft);
        Thread t2 = new Thread(lft);
        Thread t3 = new Thread(lft);
        t1.start();
        t2.start();
        t3.start();
    }
}
